
package com.eva.framework.httpfile;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import com.eva.framework.utils.LoggerFactory;

/**
 * 使用Apache的fileupload库实现的Http文件上传实用类（适用于各种富客户端上传文件，包括Andriod、iOS设备等）.
 * <p>
 * <b>注意：</b>本类中的额外数据字段是通过额外文件数据上传上来的（而非通过URL后面带的参数的方式），
 * 虽然通过上传URL后面带参数的方式给服务端最为简单，但在有些客户端库（如iOS的AFNetworking3.0库）使用时，
 * 会出现卡住的bug（这是iOS的AFNetworking3.0库的bug），所以为了通用性，本类一律不使用URL带参数的方式。
 * 
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 * @version 1.0
 * @revision 1.1, 20170306 为了规范化多平台上传文件时的额外参数上传，增加了processFileUpload2方法
 */
public class FileUpload
{
	public final static String SUCESS_KEY_NAME = "sucess";


	/**
	 * 云盘相册上传
	 */
	public static HashMap<String, Object> processFileUpload(HttpServletRequest request
			, HttpServletResponse response, String saveToDir
			, int THRESHOLD_SIZE, int MAX_FILE_SIZE, int MAX_REQUEST_SIZE) throws Exception{
		HashMap<String, Object> parametersFromClient = new HashMap<>();
		// sucess参数为true表示本次文件上传处理成功，否则表示处理失败
		parametersFromClient.put(SUCESS_KEY_NAME, Boolean.FALSE);
		if(saveToDir == null)
			throw new Exception("[HTTP文件上传] saveToDir == null!");

		if (!ServletFileUpload.isMultipartContent(request))
		{
			LoggerFactory.getLog().error("[HTTP文件上传] 没有需要上传的文件，ServletFileUpload.isMultipartContent(request)!=true!");
			throw new IOException("[HTTP文件上传] Not file to upload!");
		}
		// configures upload settings
		DiskFileItemFactory factory = new DiskFileItemFactory();
		factory.setSizeThreshold(THRESHOLD_SIZE);
		factory.setRepository(new File(System.getProperty("java.io.tmpdir")));

		ServletFileUpload upload = new ServletFileUpload(factory);
		upload.setFileSizeMax(MAX_FILE_SIZE);
		upload.setSizeMax(MAX_REQUEST_SIZE);
		// constructs the directory path to store upload file
		String uploadPath = saveToDir;//getServletContext().getRealPath("") + File.separator + UPLOAD_DIRECTORY;
		// creates the directory if it does not exist
		String groupId = request.getHeader("groupId");
		String token=request.getHeader("token");
		LoggerFactory.getLog().error(token);

		parametersFromClient.put("group_id", groupId);
		File uploadDir = new File(String.format(uploadPath,groupId));
		if (!uploadDir.exists()) {
			uploadDir.mkdirs();
		}
		try
		{
			// parses the request's content to extract file data
			List formItems = upload.parseRequest(request);
			Iterator iter = formItems.iterator();

			// 遍历获取除文件数据本身以外的额外参数（相关原理，请见Apache的开源fileupload组件官方文档）
			// iterates over form's fields
			while (iter.hasNext())
			{
				FileItem item = (FileItem) iter.next();
				// processes only fields that are not form fields
				if (!item.isFormField())
				{
					String fileName = new File(item.getName()).getName();
					String filePath =  uploadDir.getAbsolutePath()+ File.separator + fileName;
					File storeFile = new File(filePath);

					// saves the file on disk
					item.write(storeFile);
				}
				// 上传的额外参数
				else {
					String fieldName = item.getFieldName();
					String fieldValue = item.getString();
					LoggerFactory.getLog().debug("[HTTP文件上传] 已读取到额外参数：fieldName="
							+fieldName+", fieldValue="+fieldValue);

					// 把参数放入返回数据的map中
					parametersFromClient.put(fieldName, fieldValue);
				}
			}
			parametersFromClient.put(SUCESS_KEY_NAME, Boolean.TRUE);
		}
		catch (Exception ex)
		{
			LoggerFactory.getLog().error("[HTTP文件上传] 处理文件上传时出错了，"
					+ex.getMessage(), ex);
			throw ex;
		}

		return parametersFromClient;
	}


	/**
	 * 文件上传处理实现方法。
	 * 
	 * @param saveToDir 文件将要保存到的目录
	 * @param THRESHOLD_SIZE 磁盘临时缓存文件阀值。
	 * <pre>
	 *         Apache文件上传组件在解析上传数据中的每个字段内容时，需要临时保存解析出的数据，以
	 *     便在后面进行数据的进一步处理（保存在磁盘特定位置或插入数据库）。因为Java虚拟机默
	 *     认可以使用的内存空间是有限的，超出限制时将会抛出“java.lang.OutOfMemoryError”错
	 *     误。如果上传的文件很大，例如800M的文件，在内存中将无法临时保存该文件内容，Apache
	 *     文件上传组件转而采用临时文件来保存这些数据；但如果上传的文件很小，例如600个字节的
	 *     文件，显然将其直接保存在内存中性能会更加好些。
	 * 
	 *         setSizeThreshold方法用于设置是否将上传文件已临时文件的形式保存在磁盘的临界值（以
	 *     字节为单位的int值），如果从没有调用该方法设置此临界值，将会采用系统默认值10KB。对
	 *     应的getSizeThreshold() 方法用来获取此临界值。
	 * </pre>
	 * @param MAX_FILE_SIZE 单个文件最大大小
	 * @param MAX_REQUEST_SIZE 所有文件总大小（因为本方法支持多文件上传）
	 * @return 从Multipart Content中取出客户端传过来的参数
	 * @throws Exception
	 */
	public static HashMap<String, Object> processFileUpload2(HttpServletRequest request
			, HttpServletResponse response, String saveToDir
			, int THRESHOLD_SIZE, int MAX_FILE_SIZE, int MAX_REQUEST_SIZE) throws Exception
	{
		// 客户端上传文件时额外上来的参数存放于此map中
		// * 因http 文件上传的特殊性，没有办法在单独的方法里提取出额外的参数，所以只
		// * 能使用此方法在所有数据上传完成后才能保证完整拿到上传的额外参数
		HashMap<String, Object> parametersFromClient = new HashMap<String, Object>();
		
		// sucess参数为true表示本次文件上传处理成功，否则表示处理失败
		parametersFromClient.put(SUCESS_KEY_NAME, Boolean.FALSE);
		
//		boolean sucess = false;
		
		if(saveToDir == null)
			throw new Exception("[HTTP文件上传] saveToDir == null!");
		
		// checks if the request actually contains upload file
		if (!ServletFileUpload.isMultipartContent(request))
		{
//			PrintWriter writer = response.getWriter();
//			writer.println("Request does not contain upload data");
//			writer.flush();
//			return;
			LoggerFactory.getLog().error("[HTTP文件上传] 没有需要上传的文件，ServletFileUpload.isMultipartContent(request)!=true!");
			throw new IOException("[HTTP文件上传] Not file to upload!");
		}

		// configures upload settings
		DiskFileItemFactory factory = new DiskFileItemFactory();
		factory.setSizeThreshold(THRESHOLD_SIZE);
		factory.setRepository(new File(System.getProperty("java.io.tmpdir")));

		ServletFileUpload upload = new ServletFileUpload(factory);
		upload.setFileSizeMax(MAX_FILE_SIZE);
		upload.setSizeMax(MAX_REQUEST_SIZE);

		// constructs the directory path to store upload file
		String uploadPath = saveToDir;//getServletContext().getRealPath("") + File.separator + UPLOAD_DIRECTORY;
		// creates the directory if it does not exist
		File uploadDir = new File(uploadPath);
		if (!uploadDir.exists())
		{
			uploadDir.mkdirs();
		}

		try
		{
			// parses the request's content to extract file data
			List formItems = upload.parseRequest(request);
			Iterator iter = formItems.iterator();

			// 遍历获取除文件数据本身以外的额外参数（相关原理，请见Apache的开源fileupload组件官方文档）
			// iterates over form's fields
			while (iter.hasNext())
			{
				FileItem item = (FileItem) iter.next();
				// processes only fields that are not form fields
				if (!item.isFormField())
				{
					String fileName = new File(item.getName()).getName();
					String filePath = uploadPath + File.separator + fileName;
					File storeFile = new File(filePath);

					// saves the file on disk
					item.write(storeFile);
				}
				// 上传的额外参数
				else
				{
					String fieldName = item.getFieldName();
					String fieldValue = item.getString();
					
					LoggerFactory.getLog().debug("[HTTP文件上传] 已读取到额外参数：fieldName="
							+fieldName+", fieldValue="+fieldValue);
					
					// 把参数放入返回数据的map中
					parametersFromClient.put(fieldName, fieldValue);
				}
			}
//			request.setAttribute("message", "Upload has been done successfully!");
			
//			sucess = true;
			parametersFromClient.put(SUCESS_KEY_NAME, Boolean.TRUE);
		}
		catch (Exception ex)
		{
//			ex.printStackTrace();
			LoggerFactory.getLog().error("[HTTP文件上传] 处理文件上传时出错了，"
					+ex.getMessage(), ex);
			throw ex;
//			request.setAttribute("message", "There was an error: " + ex.getMessage());
		}
		
		return parametersFromClient;
	}
}
